fix: an unknown exchange rate is unknown, not zero - #8
Merged
Conversation
buildPriceInfo fell back to multiplying by zero when the token was not in
the exchange rate cache:
priceUSD = roundTo(priceUSD != null ? priceUSD
: price * (cache.containsKey(token) ? cache.get(token) : 0));
so a price in a token we have no rate for was published as a confident
$0.00, indistinguishable from a genuinely free item. _PriceInfo
consumers already render a null usd as "n/a" - see BaseTransformer - so
null is the value this should produce.
Measured in soonmarket_realtime_event: 49 activity rows carry a non zero
price and a zero usd in a token with no exchange rate at all, across
FOOBAR, EASY, CYPHR, RDM and PIXEL.
The other 9737 zero usd rows are not this bug and must not be "repaired"
away. XPR, XUSDC and LOAN all have rates, so the fallback never fired
for them. Their zeros come from roundTo defaulting to 2 decimals with
amounts genuinely below half a cent: every one of the 9724 XPR rows is
between 0.0001 and 5 XPR, and XPR is $0.00286. A bad cached rate would
not respect that ceiling, which is what rules it out.
roundTo unboxes its argument, so the derived values cannot simply be
computed and rounded once priceUSD may be null. royaltyUSD, marketFeeUSD
and sellerReceivedPriceUSD are each guarded instead - the last one
dereferenced priceUSD unguarded and would otherwise have turned this fix
into a new NPE.
No repair is possible or needed for the 49 rows: no rate exists for
those tokens, so null is the correct value and this produces it going
forward.
Refs #7
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7.
buildPriceInfofell back to multiplying by zero when the token was not in the exchange rate cache:So a price in a token we have no rate for was published as a confident
$0.00— indistinguishable from a genuinely free item._PriceInfoconsumers already render a null usd as"n/a"(seeBaseTransformer), so null is the value this should produce; the convention already exists and this converges onto it.Scope, measured
49 activity rows in
soonmarket_realtime_eventcarry a non-zero price and a zero usd in a token with no exchange rate at all — FOOBAR (21), EASY (17), CYPHR (6), RDM (3), PIXEL (2).The other 9737 zero-usd rows are not this bug and must not be "repaired" away. XPR, XUSDC and LOAN all have rates, so the fallback never fired for them. Their zeros come from
roundTodefaulting to 2 decimals with amounts genuinely below half a cent — every one of the 9724 XPR rows is between 0.0001 and 5 XPR, and XPR is $0.00286. A bad cached rate would not respect that ceiling, which is what rules it out. This is spelled out in #7 along with two hypotheses that were tested and disproved.The part that would bite a careless fix
roundTounboxes itsDoubleargument, so the derived values cannot simply be computed and rounded oncepriceUSDmay be null. Three of them are now guarded:royaltyUSDandmarketFeeUSD— were guarded onroyalty/marketFeebut not onpriceUSDsellerReceivedPriceUSD— dereferencedpriceUSDunguarded, so swapping the: 0for null without touching this would have turned the fix into a new NPE on exactly the rows it is meant to fixAlso note
cache.get(token)replacescontainsKey(...) ? get(...) : 0. Besides being the actual fix, that removes an unboxing NPE if the map ever held a null value for a present key.No repair
The 49 rows cannot be repaired — no rate exists for those tokens, so null is the correct value and this produces it going forward. The other 9737 are already correct.
Verification
This repo has no build of its own (CI checks line endings only), so it was verified through the consumers:
event-processor-contract—mvn testpasses with this source vendored insoon-market-api—mvn compilepasses. Itsmvn testfails locally on a Quarkus startupTimeoutExceptionfrom the DB connection pool, which is an environment issue unrelated to this changeConsumers need the usual submodule bump afterwards; at minimum
event-processor-contractandsoon-market-api, which are the two that render prices.🤖 Generated with Claude Code